package com.example.lockLearning;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 你在哪段代码前加锁，锁就只保护那一段；
 * 未加锁的代码不受影响，可以被多个线程同时访问；
 * 所以 Lock 控制的是“逻辑区域”而不是“对象本身”。
 */
public class Lock1 {

    /**
     * 1. `lock.lock()` 获取锁，防止其他线程进入临界区。
     * 2. `try` 语句块保证发生异常时也会释放锁（防止死锁）。
     * 3. `lock.unlock()` 释放锁，允许其他线程进入。
     */
    /**
     * 使用 ReentrantLock 时，我们需要手动调用 lock.lock() 方法，这个方法的作用是：
     * 如果锁是空闲的（没有线程持有），当前线程立即获得锁；
     * 如果锁已被其他线程持有，当前线程将阻塞等待，直到获得锁；
     * 加锁成功后，当前线程成为“持有者”，其他线程不能进入这段加锁的代码，直到你调用 lock.unlock()。
     */
    private final Lock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        /**
         * lock 是给代码段上锁，锁本身归某个线程“暂时所有”，这段代码在持锁期间就只能由这个线程执行。
         */
        lock.lock();// 上锁
        try {
            /**
             * 临界区
             */
            for (int i = 0; i < 5; i++) {
                count++;
                System.out.println(Thread.currentThread().getName() + "-" + count);
            }
        } finally {
            /**
             * 如果临界区代码发生异常，程序可能跳过 unlock()，造成锁永远不释放，出现死锁；
             * 用 finally 保证无论如何都会解锁，非常重要！
             */
            lock.unlock();// 解锁
        }
    }

    /**
     * 错误代码	                        问题
     * 没有调用 unlock()	                会导致永久死锁
     * lock() 放在 if 里面	            可能不会加锁就进入了临界区
     * lock.unlock() 没有放在 finally 里	异常时不会释放锁
     * 多个方法加锁，但锁不是同一个对象	    加锁无效
     *
     * @param args
     */
    public static void main(String[] args) {

        Lock1 lock1 = new Lock1();

        /**
         * 多个线程加锁访问共享资源
         */
        Thread t1 = new Thread(lock1::increment);
        Thread t2 = new Thread(lock1::increment);

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        t2.start();

    }
}
